package com.demandbreakdown.xiyun.service;


import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.demandbreakdown.xiyun.common.base.ResultCode;
import com.demandbreakdown.xiyun.common.exception.BusinessException;
import com.demandbreakdown.xiyun.strategy.StorageStrategy;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import static com.demandbreakdown.xiyun.common.constant.FileConstant.*;
import static com.demandbreakdown.xiyun.common.constant.PictureConstant.*;

/**
 * 存储服务门面
 *
 * @author poxz
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class StorageService {

    // 存储策略
    private final StorageStrategy storageStrategy;

    /**
     * 上传图片
     *
     * @param file    图片文件
     * @param userId  用户ID
     * @param spaceId 空间ID
     * @return PictureVO
     */
    public String uploadPicture(MultipartFile file, Long userId, Long spaceId) {
        log.info("开始上传图片: userId={}, spaceId={}, fileName={}, size={}KB",
                userId, spaceId, file.getOriginalFilename(), file.getSize() / 1024);

        // 验证文件
        validatePictureFile(file);

        // 生成路径
        String filePath = generatePicturePath(userId, spaceId, file.getOriginalFilename());

        // 上传文件
        try {
            String fileUrl = storageStrategy.upload(file.getInputStream(), filePath, file.getContentType());
            log.info("图片上传成功: userId={}, spaceId={}, url={}", userId, spaceId, fileUrl);
            return fileUrl;
        } catch (Exception e) {
            log.error("图片上传失败: userId={}, spaceId={}, fileName={}",
                    userId, spaceId, file.getOriginalFilename(), e);
            throw new BusinessException(ResultCode.SYSTEM_ERROR, "图片上传失败");
        }
    }

    /**
     * 上传资源（头像、空间封面等）
     *
     * @param file      资源文件
     * @param assetType 资源类型
     * @param userId    用户ID
     * @return 资源访问URL
     */
    public String uploadAsset(MultipartFile file, String assetType, Long userId) {
        log.info("开始上传资源: type={}, userId={}, fileName={}, size={}KB",
                assetType, userId, file.getOriginalFilename(), file.getSize() / 1024);

        // 验证文件
        validateAssetFile(file, assetType);

        // 生成路径
        String filePath = generateAssetPath(userId, assetType, file.getOriginalFilename());

        // 上传文件
        try {
            String fileUrl = storageStrategy.upload(file.getInputStream(), filePath, file.getContentType());
            log.info("资源上传成功: type={}, userId={}, url={}", assetType, userId, fileUrl);
            return fileUrl;
        } catch (Exception e) {
            log.error("资源上传失败: userId={}, assetType={}, fileName={}",
                    userId, assetType, file.getOriginalFilename(), e);
            throw new BusinessException(ResultCode.SYSTEM_ERROR, "资源上传失败");
        }
    }

    /**
     * 生成图片路径
     *
     * @param userId       用户ID
     * @param spaceId      空间ID
     * @param originalName 文件原始名称
     * @return 图片路径
     */
    private String generatePicturePath(Long userId, Long spaceId, String originalName) {
        // 提取扩展名
        String extension = FileUtil.extName(originalName);
        if (StrUtil.isBlank(extension)) {
            extension = DEFAULT_EXTENSION;
        }

        // 生成唯一文件名
        String fileName = IdUtil.randomUUID() + "." + extension;

        // 生成日期目录
        String datePath = LocalDateTime.now().format(
                DateTimeFormatter.ofPattern(DATE_PATH_FORMAT)
        );

        // 组装路径（使用常量）
        if (spaceId == null) {
            // 公共空间
            return String.format(PICTURE_PUBLIC_PATH_FORMAT, userId, datePath, fileName);
        } else {
            // 私有空间
            return String.format(PICTURE_PRIVATE_PATH_FORMAT, userId, spaceId, datePath, fileName);
        }
    }

    /**
     * 生成资源路径
     *
     * @param userId           用户ID
     * @param assetType        资源类型
     * @param originalFilename 文件原始名称
     * @return 资源路径
     */
    private String generateAssetPath(Long userId, String assetType, String originalFilename) {
        // 提取扩展名
        String extension = FileUtil.extName(originalFilename);
        if (StrUtil.isBlank(extension)) {
            extension = DEFAULT_EXTENSION;
        }

        // 生成唯一文件名
        String fileName = IdUtil.randomUUID() + "." + extension;

        // 根据资源类型生成路径（使用常量）
        return switch (assetType) {
            case ASSET_TYPE_AVATAR -> String.format(AVATAR_PATH_FORMAT, userId, fileName);
            case ASSET_TYPE_COVER -> String.format(COVER_PATH_FORMAT, userId, fileName);
            default -> String.format("%s/%d/%s", assetType, userId, fileName);
        };
    }

    /**
     * 删除文件
     *
     * @param fileUrl 文件URL
     */
    public void deleteFile(String fileUrl) {
        if (StrUtil.isBlank(fileUrl)) {
            log.warn("文件URL为空，跳过删除");
            return;
        }

        // 从URL提取文件路径
        String filePath = extractFilePathFromUrl(fileUrl);
        if (StrUtil.isBlank(filePath)) {
            log.warn("无法从URL提取文件路径: {}", fileUrl);
            return;
        }

        // 调用策略删除
        storageStrategy.delete(filePath);
        log.info("文件删除成功: {}", filePath);
    }

    /**
     * 生成文件下载链接
     *
     * @param fileUrl  文件访问URL
     * @param fileName 下载时的文件名
     * @param response HTTP响应对象
     */
    public void generateDownloadUrl(String fileUrl, String fileName, HttpServletResponse response) {
        log.info("生成下载链接: fileUrl={}, fileName={}", fileUrl, fileName);

        // 调用策略生成下载链接
        storageStrategy.generateDownloadUrl(fileUrl, fileName, response);
    }

    // ========== 验证方法（私有）==========

    /**
     * 验证图片文件
     *
     * @param file 文件对象
     */
    private void validatePictureFile(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException(ResultCode.PARAMS_ERROR, "文件不能为空");
        }

        // 检查文件大小
        if (file.getSize() > MAX_PICTURE_SIZE) {
            throw new BusinessException(ResultCode.PICTURE_SIZE_EXCEEDED,
                    "图片大小不能超过 " + (MAX_PICTURE_SIZE / 1024 / 1024) + "MB");
        }

        // 检查文件类型
        String contentType = file.getContentType();
        if (contentType == null || !contentType.startsWith(CONTENT_TYPE_IMAGE_PREFIX)) {
            throw new BusinessException(ResultCode.PICTURE_FORMAT_ERROR, "只能上传图片文件");
        }

        // 检查文件名和扩展名
        String filename = file.getOriginalFilename();
        if (StrUtil.isBlank(filename)) {
            throw new BusinessException(ResultCode.PARAMS_ERROR, "文件名不能为空");
        }

        String extension = FileUtil.extName(filename).toLowerCase();
        if (StrUtil.isBlank(extension) || !SUPPORTED_IMAGE_FORMATS.contains(extension)) {
            throw new BusinessException(ResultCode.PICTURE_FORMAT_ERROR,
                    "不支持的图片格式，仅支持：" + String.join("、", SUPPORTED_IMAGE_FORMATS));
        }
    }

    /**
     * 验证资源文件（头像、封面）
     *
     * @param file      文件对象
     * @param assetType 资源类型
     */
    private void validateAssetFile(MultipartFile file, String assetType) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException(ResultCode.PARAMS_ERROR, "文件不能为空");
        }

        // 根据资源类型设置大小限制
        long maxSize;
        String sizeTip;
        if (ASSET_TYPE_AVATAR.equals(assetType)) {
            maxSize = MAX_AVATAR_SIZE;
            sizeTip = (MAX_AVATAR_SIZE / 1024 / 1024) + "MB";
        } else if (ASSET_TYPE_COVER.equals(assetType)) {
            maxSize = MAX_COVER_SIZE;
            sizeTip = (MAX_COVER_SIZE / 1024 / 1024) + "MB";
        } else {
            maxSize = MAX_COVER_SIZE;
            sizeTip = (MAX_COVER_SIZE / 1024 / 1024) + "MB";
        }

        if (file.getSize() > maxSize) {
            throw new BusinessException(ResultCode.PICTURE_SIZE_EXCEEDED,
                    assetType + "大小不能超过 " + sizeTip);
        }

        // 检查文件类型
        String contentType = file.getContentType();
        if (contentType == null || !contentType.startsWith(CONTENT_TYPE_IMAGE_PREFIX)) {
            throw new BusinessException(ResultCode.PICTURE_FORMAT_ERROR);
        }
    }

    // ========== 工具方法（私有）==========


    /**
     * 从URL提取文件路径
     *
     * @param fileUrl 文件完整URL
     * @return 文件相对路径
     */
    private String extractFilePathFromUrl(String fileUrl) {
        if (StrUtil.isBlank(fileUrl)) {
            return null;
        }

        // 假设URL格式：https://xxx.cos.ap-chengdu.myqcloud.com/pictures/123/456/uuid.jpg
        // 需要提取：pictures/123/456/uuid.jpg

        // 查找域名后的路径部分
        int pathStart = fileUrl.indexOf('/', HTTPS_PROTOCOL_PREFIX_LENGTH);
        if (pathStart > 0) {
            return fileUrl.substring(pathStart + 1);
        }

        return null;
    }
}
